using System;
using System.Collections.Generic;
using System.Threading;
using HslCommunication.Core;

namespace HslCommunication.Algorithms.ConnectPool
{
	/// <summary>
	/// 一个连接池管理器，负责维护多个可用的连接，并且自动清理，扩容
	/// </summary>
	/// <typeparam name="TConnector">管理的连接类，需要支持IConnector接口</typeparam>
	/// <remarks>
	/// 需要先实现 <see cref="T:HslCommunication.Algorithms.ConnectPool.IConnector" /> 接口的对象，然后就可以实现真正的连接池了，理论上可以实现任意的连接对象，包括modbus连接对象，各种PLC连接对象，数据库连接对象，redis连接对象，SimplifyNet连接对象等等。下面的示例就是modbus-tcp的实现
	/// <note type="warning">要想真正的支持连接池访问，还需要服务器支持一个端口的多连接操作，三菱PLC的端口就不支持，如果要测试示例代码的连接池对象，需要使用本组件的<see cref="T:HslCommunication.ModBus.ModbusTcpServer" />来创建服务器对象</note>
	/// </remarks>
	/// <example>
	/// 下面举例实现一个modbus的连接池对象，先实现接口化的操作
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Algorithms\ConnectPool.cs" region="IConnector Example" title="IConnector示例" />
	/// 然后就可以实现真正的连接池了
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Algorithms\ConnectPool.cs" region="ConnectPoolExample" title="ConnectPool示例" />
	/// </example>
	public class ConnectPool<TConnector> where TConnector : IConnector
	{
		private Func<TConnector> CreateConnector = null;

		private int maxConnector = 10;

		private int usedConnector = 0;

		private int expireTime = 30;

		private bool canGetConnector = true;

		private Timer timerCheck = null;

		private SimpleHybirdLock hybirdLock = null;

		private List<TConnector> connectors = null;

		/// <summary>
		/// 获取或设置最大的连接数
		/// </summary>
		public int MaxConnector
		{
			get
			{
				return maxConnector;
			}
			set
			{
				maxConnector = value;
			}
		}

		/// <summary>
		/// 获取或设置连接过期的时间，单位秒，默认30秒
		/// </summary>
		public int ConectionExpireTime
		{
			get
			{
				return expireTime;
			}
			set
			{
				expireTime = value;
			}
		}

		/// <summary>
		/// 当前已经使用的连接数
		/// </summary>
		public int UsedConnector => usedConnector;

		/// <summary>
		/// 实例化一个连接池对象，需要指定如果创建新实例的方法
		/// </summary>
		/// <param name="createConnector">创建连接对象的委托</param>
		public ConnectPool(Func<TConnector> createConnector)
		{
			CreateConnector = createConnector;
			hybirdLock = new SimpleHybirdLock();
			connectors = new List<TConnector>();
			timerCheck = new Timer(TimerCheckBackground, null, 10000, 30000);
		}

		/// <summary>
		/// 获取可用的对象
		/// </summary>
		/// <returns>可用的连接对象</returns>
		public TConnector GetAvailableConnector()
		{
			while (!canGetConnector)
			{
				Thread.Sleep(100);
			}
			TConnector val = default(TConnector);
			hybirdLock.Enter();
			for (int i = 0; i < connectors.Count; i++)
			{
				if (!connectors[i].IsConnectUsing)
				{
					TConnector val2 = connectors[i];
					val2.IsConnectUsing = true;
					val = connectors[i];
					break;
				}
			}
			if (val == null)
			{
				val = CreateConnector();
				val.IsConnectUsing = true;
				val.LastUseTime = DateTime.Now;
				val.Open();
				connectors.Add(val);
				usedConnector = connectors.Count;
				if (usedConnector == maxConnector)
				{
					canGetConnector = false;
				}
			}
			val.LastUseTime = DateTime.Now;
			hybirdLock.Leave();
			return val;
		}

		/// <summary>
		/// 使用完之后需要通知管理器
		/// </summary>
		/// <param name="connector">连接对象</param>
		public void ReturnConnector(TConnector connector)
		{
			hybirdLock.Enter();
			int num = connectors.IndexOf(connector);
			if (num != -1)
			{
				TConnector val = connectors[num];
				val.IsConnectUsing = false;
			}
			hybirdLock.Leave();
		}

		private void TimerCheckBackground(object obj)
		{
			hybirdLock.Enter();
			for (int num = connectors.Count - 1; num >= 0; num--)
			{
				if ((DateTime.Now - connectors[num].LastUseTime).TotalSeconds > (double)expireTime && !connectors[num].IsConnectUsing)
				{
					connectors[num].Close();
					connectors.RemoveAt(num);
				}
			}
			usedConnector = connectors.Count;
			if (usedConnector < MaxConnector)
			{
				canGetConnector = true;
			}
			hybirdLock.Leave();
		}
	}
}
